﻿using Mapster;
using PLC.Interface.Recipes;
using PLC.Interface.RecipeVariables;
using PLC.Interface.Stations;
using PLC.Interface.StationVariables;
using PLCWpfTest.Common;
using PLCWpfTest.Common.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using YWM.Common.Exceptions;

namespace PLCWpfTest.ViewModels
{
    public class RecipeManagementViewModel : BaseViewModel
    {
        private readonly IRecipeAppService _recipeAppService;

        private readonly IRecipeVariableAppService _recipeVariableAppService;

        public AsyncRelayCommand SelectCommand { get; set; }

        public AsyncRelayCommand<(RecipeResultDto, DatabaseMethod)> ApplyCommand { get; set; }

        private IReadOnlyList<StationResultDto> _stations;
        /// <summary>
        /// 站点集合
        /// </summary>
        public IReadOnlyList<StationResultDto> Stations
        {
            get { return _stations; }
            set { SetProperty(ref _stations, value); }
        }

        private IReadOnlyList<RecipeResultDto> _recipes = new List<RecipeResultDto>();
        /// <summary>
        /// 配方集合
        /// </summary>
        public IReadOnlyList<RecipeResultDto> Recipes
        {
            get { return _recipes; }
            set { SetProperty(ref _recipes, value); }
        }

        private RecipeResultDto _defaultRecipe;
        /// <summary>
        /// 默认配方
        /// </summary>
        public RecipeResultDto DefaultRecipe
        {
            get { return _defaultRecipe; }
            set { SetProperty(ref _defaultRecipe, value); }
        }

        private RecipeResultDto _currentRecipe = new RecipeResultDto();
        /// <summary>
        /// 当前配方
        /// </summary>
        public RecipeResultDto CurrentRecipe
        {
            get { return _currentRecipe; }
            set { SetProperty(ref _currentRecipe, value); }
        }

        /// <summary>
        /// 配方变量集合
        /// </summary>
        private IReadOnlyList<RecipeVariableDto> _recipeVariables;

        public RecipeManagementViewModel(
            IRecipeAppService recipeAppService,
            IRecipeVariableAppService recipeVariableAppService,
            AsyncRelayCommand selectRecipeCommand,
            AsyncRelayCommand<(RecipeResultDto, DatabaseMethod)> applyCommand)
        {
            _recipeAppService = recipeAppService;
            _recipeVariableAppService = recipeVariableAppService;
            SelectCommand = selectRecipeCommand;
            SelectCommand.Executing += SelectCommand_Executing;
            ApplyCommand = applyCommand;
            ApplyCommand.BeforeCanExecute += ApplyCommand_BeforeCanExecute;
            ApplyCommand.BeforeExecute += ApplyCommand_BeforeExecute;
            ApplyCommand.Executing += ApplyCommand_Executing;
            ApplyCommand.UnexecutedFriendlyException += ApplyCommand_UnexecutedFriendlyException;
            Stations = DataCache.Stations;
        }

        protected override async Task LoadCommand_Executing(object parameter)
        {
            await SearchAsync();
        }

        private async Task SearchAsync()
        {
            Recipes = await _recipeAppService.GetList();
            CurrentRecipe = new RecipeResultDto();
            RecipeResultDto recipe = Recipes.FirstOrDefault(r => r.IsDefault);
            //有默认配方
            if (recipe != null)
            {
                DefaultRecipe = recipe;
            }
            SetStationsByPLC();
        }

        private async Task SelectCommand_Executing(object parameter)
        {
            RecipeResultDto recipe = (RecipeResultDto)parameter;
            //没有选择
            if (recipe == null)
            {
                SetStationsByPLC();
                return;
            }
            if (!recipe.Id.Equals(CurrentRecipe.Id))
            {
                CurrentRecipe = recipe.Adapt<RecipeResultDto>();
                await SetStations(CurrentRecipe);
            }
        }

        private async Task SetStations(RecipeResultDto recipe)
        {
            _recipeVariables = await _recipeVariableAppService.GetList(recipe.Id);
            IReadOnlyList<StationResultDto> stations = new List<StationResultDto>(Stations);
            foreach (StationResultDto station in Stations)
            {
                StationVariableResultDto stationVariable = station.StationVariable;
                stationVariable.TempHighLimitValue = (_recipeVariables.FirstOrDefault(rv => rv.VariableId == stationVariable.TempHighLimitId)?.Value ?? "0").ToDouble();
                stationVariable.TempLowLimitValue = (_recipeVariables.FirstOrDefault(rv => rv.VariableId == stationVariable.TempLowLimitId)?.Value ?? "0").ToDouble();
                stationVariable.HumidityHighLimitValue = (_recipeVariables.FirstOrDefault(rv => rv.VariableId == stationVariable.HumidityHighLimitId)?.Value ?? "0").ToDouble();
                stationVariable.HumidityLowLimitValue = (_recipeVariables.FirstOrDefault(rv => rv.VariableId == stationVariable.HumidityLowLimitId)?.Value ?? "0").ToDouble();
                stationVariable.TempAlarmEnableValue = (_recipeVariables.FirstOrDefault(rv => rv.VariableId == stationVariable.TempAlarmEnableId)?.Value ?? "0").ToShort();
                stationVariable.HumidityAlarmEnableValue = (_recipeVariables.FirstOrDefault(rv => rv.VariableId == stationVariable.HumidityAlarmEnableId)?.Value ?? "0").ToShort();
            }
            Stations = stations;
        }

        private void SetStationsByPLC()
        {
            if (!CommunicationCache.Connected)
            {
                return;
            }
            IReadOnlyList<StationResultDto> stations = new List<StationResultDto>(Stations);
            foreach (StationResultDto item in stations)
            {
                if (CommunicationCache.LongVariablePairs.ContainsKey(item.StationVariable.TempHighLimitId))
                {
                    item.StationVariable.TempHighLimitValue = CommunicationCache.LongVariablePairs[item.StationVariable.TempHighLimitId].Value.ToDouble();
                }
                if (CommunicationCache.LongVariablePairs.ContainsKey(item.StationVariable.TempLowLimitId))
                {
                    item.StationVariable.TempLowLimitValue = CommunicationCache.LongVariablePairs[item.StationVariable.TempLowLimitId].Value.ToDouble();
                }
                if (CommunicationCache.LongVariablePairs.ContainsKey(item.StationVariable.HumidityHighLimitId))
                {
                    item.StationVariable.HumidityHighLimitValue = CommunicationCache.LongVariablePairs[item.StationVariable.HumidityHighLimitId].Value.ToDouble();
                }
                if (CommunicationCache.LongVariablePairs.ContainsKey(item.StationVariable.HumidityLowLimitId))
                {
                    item.StationVariable.HumidityLowLimitValue = CommunicationCache.LongVariablePairs[item.StationVariable.HumidityLowLimitId].Value.ToDouble();
                }
                if (CommunicationCache.LongVariablePairs.ContainsKey(item.StationVariable.TempAlarmEnableId))
                {
                    item.StationVariable.TempAlarmEnableValue = CommunicationCache.LongVariablePairs[item.StationVariable.TempAlarmEnableId].Value.ToShort();
                }
                if (CommunicationCache.LongVariablePairs.ContainsKey(item.StationVariable.HumidityAlarmEnableId))
                {
                    item.StationVariable.HumidityAlarmEnableValue = CommunicationCache.LongVariablePairs[item.StationVariable.HumidityAlarmEnableId].Value.ToShort();
                }
            }
            Stations = stations;
        }

        private bool ApplyCommand_BeforeCanExecute(object parameter)
        {
            switch (Convert.ToInt32(parameter))
            {
                case 0:
                    return CurrentRecipe.IsValidated;
                case 1:
                case 2:
                case 3:
                    return Recipes.FirstOrDefault(r => r.Id == CurrentRecipe.Id) != null && CurrentRecipe.IsValidated;
            }
            return false;
        }

        private bool ApplyCommand_BeforeExecute(object parameter)
        {
            int type = Convert.ToInt32(parameter);
            switch (type)
            {
                case 0:
                case 1:
                    Func<RecipeResultDto, bool> predicate;
                    if (type == 0)
                    {
                        if (Recipes.Count >= 10)
                        {
                            this.ShowWarning("不能超过十条配方记录");
                            return false;
                        }
                        predicate = r => r.DisplayName.Equals(CurrentRecipe.DisplayName);
                    }
                    else
                    {
                        if (DefaultRecipe != null && DefaultRecipe.Id == CurrentRecipe.Id)
                        {
                            this.ShowWarning("不能修改默认配方");
                            return false;
                        }
                        predicate = r => r.Id != CurrentRecipe.Id && r.DisplayName.Equals(CurrentRecipe.DisplayName);
                    }
                    if (Recipes.FirstOrDefault(predicate) != null)
                    {
                        this.ShowWarning("配方名称已存在");
                        return false;
                    }
                    foreach (StationResultDto station in Stations)
                    {
                        StationVariableResultDto stationVariable = station.StationVariable;
                        if (stationVariable.TempLowLimitValue >= stationVariable.TempHighLimitValue)
                        {
                            this.ShowWarning(string.Format(Lang["温度低限不能高于温度高限"], station.Identifier));
                            return false;
                        }
                        if (stationVariable.HumidityLowLimitValue >= stationVariable.HumidityHighLimitValue)
                        {
                            this.ShowWarning(string.Format(Lang["湿度低限不能高于湿度高限"], station.Identifier));
                            return false;
                        }
                    }
                    return true;
                case 2:
                    if (DefaultRecipe != null && DefaultRecipe.Id == CurrentRecipe.Id)
                    {
                        this.ShowWarning("不能删除默认配方");
                        return false;
                    }
                    MessageBoxResult deleteResult = this.ShowQuestion(Lang["确定要删除吗"]);
                    return deleteResult == MessageBoxResult.OK;
                case 3:
                    if (!CommunicationCache.Connected)
                    {
                        this.ShowWarning("通信设备连接失败");
                        return false;
                    }
                    MessageBoxResult applyResult = this.ShowQuestion(Lang["确定要应用配方吗"]);
                    return applyResult == MessageBoxResult.OK;
            }
            return false;
        }

        private async Task<(RecipeResultDto, DatabaseMethod)> ApplyCommand_Executing(object parameter)
        {
            int type = Convert.ToInt32(parameter);
            switch (type)
            {
                case 0:
                case 1:
                default:
                    RecipeDto recipe = type == 0 ? recipe = new RecipeDto { DisplayName = CurrentRecipe.DisplayName } : CurrentRecipe;
                    RecipeResultDto recipeResult = await SaveAsync(recipe);
                    await SearchAsync();
                    return (recipeResult, recipe.Id > 0 ? DatabaseMethod.Update : DatabaseMethod.Insert);
                case 2:
                    await _recipeAppService.Delete(CurrentRecipe);
                    await SearchAsync();
                    return (CurrentRecipe, DatabaseMethod.Delete);
                case 3:
                    await CommunicationCache.ApplyRecipe(_recipeVariables);
                    CurrentRecipe.IsDefault = true;
                    await _recipeAppService.Apply(CurrentRecipe);
                    DefaultRecipe = CurrentRecipe;
                    return (CurrentRecipe, DatabaseMethod.Update);
            }
        }

        private void ApplyCommand_UnexecutedFriendlyException(FriendlyException ex)
        {
            ApplyCommand.ShowError(ex.Message);
        }

        private async Task<RecipeResultDto> SaveAsync(RecipeDto recipe)
        {
            List<RecipeVariableDto> recipeVariables = new List<RecipeVariableDto>(Stations.Count * 6);
            foreach (StationResultDto station in Stations)
            {
                StationVariableResultDto stationVariable = station.StationVariable;
                recipeVariables.Add(new RecipeVariableDto(stationVariable.TempHighLimitId, stationVariable.TempHighLimitValue.ToString()));
                recipeVariables.Add(new RecipeVariableDto(stationVariable.TempLowLimitId, stationVariable.TempLowLimitValue.ToString()));
                recipeVariables.Add(new RecipeVariableDto(stationVariable.HumidityHighLimitId, stationVariable.HumidityHighLimitValue.ToString()));
                recipeVariables.Add(new RecipeVariableDto(stationVariable.HumidityLowLimitId, stationVariable.HumidityLowLimitValue.ToString()));
                recipeVariables.Add(new RecipeVariableDto(stationVariable.TempAlarmEnableId, stationVariable.TempAlarmEnableValue.ToString()));
                recipeVariables.Add(new RecipeVariableDto(stationVariable.HumidityAlarmEnableId, stationVariable.HumidityAlarmEnableValue.ToString()));
            }
            return await _recipeAppService.Save(recipe, recipeVariables);
        }
    }
}
